package com.pudu.pudusclient;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.DataProcess.PCDataProcess;
import com.DataProcess.Robot;
import com.pudu.pdrobot.App;

import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;


public class LTSService extends Service {
    public LTSService() {
    }

    public final static int TLS_PORT = 16888;

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    private IBinder binder = new LTSBinder();

    public class LTSBinder extends Binder {
        public LTSService getService() {
            return LTSService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(App.MYTAG, "LTSService.onDestroy");
        super.onDestroy();
    }

    @Override
    public void onCreate() {
        Log.i(App.MYTAG, "LTSService.onCreate");
        mHandler = new MyTLSHandler(this);

        StartTLSServer();
    }

    MyTLSHandler mHandler;
    private static class MyTLSHandler extends Handler {
        private final WeakReference<LTSService> mServices;

        public MyTLSHandler(LTSService service) {
            mServices = new WeakReference<LTSService>(service);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x01:
                    break;
            }
        }
    }


    public void Send(byte[] data) {
        boolean isWorking = false;
        synchronized (LTSService.this) {
            isWorking = bIsWorking;

            if (isWorking == false)
                return;

            if (mSendRunnable != null) {
                mSendRunnable.enqueueSocketPacket(new MySocketPacket(data, 0x2));
            }
        }
        return;
    }

    public void Send(String data) {
        boolean isWorking = false;
        synchronized (LTSService.this) {
            isWorking = bIsWorking;

            if (isWorking == false)
                return;

            if (mSendRunnable != null) {
                mSendRunnable.enqueueSocketPacket(new MySocketPacket(data.getBytes(), 0x2));
            }
        }
    }


    ServerSocket mServer;

    boolean bIsWorking = false;

    Socket mClient;
    Handler mControlHandler;
    HandlerThread mControlHandlerThread;
    TLSSendRunnable mSendRunnable;
    TLSReceiveRunnable mReceiveRunnable;

    Thread mSendThread;
    Thread mReceiveThread;

    public void StartTLSServer()
    {
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    mServer = new ServerSocket(TLS_PORT);

                    while(true)
                    {
                        Socket client = mServer.accept();
                        if( mClient == null )
                        {
                            mClient = client;

                            mClient.setSoTimeout(1000);
                            mClient.setKeepAlive(true);

                            //setup control thread
                            ClientControlCallback controlcallback = new ClientControlCallback();
                            mControlHandlerThread = new HandlerThread("ClientSocketThread" + client);
                            mControlHandlerThread.start();
                            mControlHandler = new Handler(mControlHandlerThread.getLooper(), controlcallback);

                            //setup send thread
                            mSendRunnable = new TLSSendRunnable(client);
                            mSendThread = new Thread(mSendRunnable);
                            mSendThread.start();

                            //setup receive thread
                            mReceiveRunnable = new TLSReceiveRunnable(client);
                            mReceiveThread = new Thread(mReceiveRunnable);
                            mReceiveThread.start();

                            //set work flag
                            synchronized (LTSService.this) {
                                bIsWorking = true;
                            }

                            //set up heartbeat
//                            Message msg = mControlHandler.obtainMessage();
//                            msg.what = 0x01;
//                            mControlHandler.sendMessageDelayed(msg, 1000);

                        }
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }


    public class ClientControlCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {

            switch (msg.what) {
                case 0x01:
                    LTSService.this.Send("server heart".getBytes());

                    Message mymsg = mControlHandler.obtainMessage();
                    mymsg.what = 0x01;
                    mControlHandler.sendMessageDelayed(mymsg, 1000);
                    break;
                case 0x02:
                    boolean isWorking = false;
                    synchronized (LTSService.this) {
                        isWorking = bIsWorking;
                    }

                    try {
                        if (isWorking == true) {
                            mReceiveThread.interrupt();
                            mSendThread.interrupt();

                            try {
                                mClient.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                            mControlHandlerThread.quit();

                            mClient = null;
                        }
                    }catch(Exception e)
                    {
                        e.printStackTrace();
                    }

                    break;

            }
            return false;
        }
    }

    class TLSReceiveRunnable implements Runnable {
        Socket mSocket;

        TLSReceiveRunnable(Socket socket) {
            mSocket = socket;
        }

        @Override
        public void run() {

            InputStream inputReader = null;
            byte[] result = new byte[1024];
            while (mSocket.isConnected() && !Thread.interrupted()) {

                try {
                    inputReader = mSocket.getInputStream();
                    int len = inputReader.read(result);
                    String strData = new String(result, 0, len);
                    if( strData != null && strData.isEmpty() == false) {
                        Log.i("kkkkk", strData);

                        try {
                            JSONObject myData = new JSONObject(strData);
                            Iterator<?> it = myData.keys();

                            int nType = 0;
                            while(it.hasNext()){
                                String strKey = (String) it.next().toString();
                                String strValue = myData.getString(strKey);

                                if(strKey.compareTo("type") == 0
                                        && strValue.compareTo("set_value") == 0)
                                {
                                    //continue for set value
                                    nType = 1;
                                    continue;
                                }
                                else if(strKey.compareTo("type") == 0
                                        && strValue.compareTo("set_speed") == 0)
                                {
                                    nType = 2;
                                    //continue for set value
                                    continue;
                                }
                                else if(strKey.compareTo("type") == 0
                                        && strValue.compareTo("set_control") == 0)
                                {
                                    nType = 3;
                                    //continue for set value
                                    continue;
                                }
                                else if(strKey.compareTo("type") == 0
                                        && strValue.compareTo("get_value") == 0)
                                {
                                    //continue for get value
                                    String strGobalConfigData = PCDataProcess.getGlobalValue();
                                    Send(strGobalConfigData);
                                    break;
                                }
                                else
                                {
                                    //SetValue;
                                    if(nType == 1)
                                        PCDataProcess.setGlobalValue(strKey, strValue);
                                    else if (nType == 2)
                                        PCDataProcess.getSpeedFromPC(strKey, strValue);
                                    else if (nType == 3)
                                        PCDataProcess.getControlFromPC(strKey,strValue);
                                    else ;
                                    
                                    continue;
                                }

                            }
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                        }

                    }

                } catch (SocketTimeoutException e) {
                    continue;
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }

            Message msg = mControlHandler.obtainMessage();
            msg.what = 0x02;
            mControlHandler.sendMessage(msg);

            Log.i("aaaa", "ReceiveRunnable quit");
        }
    }

    class TLSSendRunnable implements Runnable {
        Socket mSocket;

        private LinkedBlockingQueue<MySocketPacket> sendingPacketQueue;

        TLSSendRunnable(Socket socket) {
            mSocket = socket;
        }

        public void enqueueSocketPacket(final MySocketPacket socketPacket) {
            try {
                getSendingPacketQueue().put(socketPacket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        protected LinkedBlockingQueue<MySocketPacket> getSendingPacketQueue() {
            if (sendingPacketQueue == null) {
                sendingPacketQueue = new LinkedBlockingQueue<MySocketPacket>();
            }
            return sendingPacketQueue;
        }

        @Override
        public void run() {

            MySocketPacket packet;
            try {
                while (!Thread.interrupted() && mSocket.isConnected() &&
                        (packet = getSendingPacketQueue().take()) != null) {

                    InetAddress address = mSocket.getInetAddress();

                    if(packet.getType() == 0x1) {
                        if (address.isReachable(1000) == true) {
                            Log.i("aaaa", "isReachable true. ");
                        } else {
                            Log.i("aaaa", "isReachable fail. ");
                            throw new Exception("isReachable failed");
                        }
                    }

                    {
                        OutputStream out = mSocket.getOutputStream();
                        out.write(packet.getData());
                        out.flush();
                        Writer myout = new OutputStreamWriter(out);
                        myout.flush();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            Message msg = mControlHandler.obtainMessage();
            msg.what = 0x02;
            mControlHandler.sendMessage(msg);

            Log.i("aaaa", "TLSSendRunnable quit");
        }
    }
}
